package com.ruoyi.lowcode.service.impl;

import com.fasterxml.jackson.databind.ObjectMapper;

import com.ruoyi.lowcode.constants.VoteConstants;
import com.ruoyi.lowcode.service.VoteService;
import com.ruoyi.lowcode.vo.VoteEchartVo;
import com.ruoyi.lowcode.vo.VoteVo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.DefaultTypedTuple;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.concurrent.TimeUnit;

@Service
@Slf4j
public class VoteServiceImpl implements VoteService {

    @Autowired
    RedisTemplate redisTemplate;

    @Autowired
    ObjectMapper objectMapper;

    @Autowired
    RabbitTemplate rabbitTemplate;

    @Override
    public HashMap<String, Object> doVote(String phone, String name) throws Exception {

        //0. 判断name是不是合法的候选人
        checkCandidateName(name);

        //1. 使用手机号给指定候选人投票，要求每个手机号每分钟只能投一票
        checkCanVote(phone);
        //2. 把投票信息写入Redis 的 Zset 数据结构中
        String key = VoteConstants.VOTE_DOVOTE_ZSET_KEY;
        Double voteNum = redisTemplate.opsForZSet().incrementScore(key, name, 1);

        //3. 准备hashmap并且返回
        HashMap<String, Object> data = new HashMap<>();
        data.put("candidateName",name);
        data.put("totalnum",voteNum);
        log.info("手机号{}投票成功了，候选人{}票数为{}",phone,name,voteNum);

        //4. 准备要写入mq的消息的json和routingkey
        HashMap<String, Object> mqJsonMap = new HashMap<>();
        mqJsonMap.put("phone",phone);
        mqJsonMap.put("candidateName",name);
        mqJsonMap.put("votetime",new Date());

        // map ---> json
        String json = objectMapper.writeValueAsString(mqJsonMap);
        String routingkey = "vote."+phone;
        String exchangename = "vote-exchange";
        rabbitTemplate.convertAndSend(exchangename,routingkey,json);
        log.info("发送消息到mq完成，msg={};routingkey={};exchangename={}",json,routingkey,exchangename);
        return data;
    }

    /**
     * 检查候选人名字是否合法
     *
     * @param name
     */
    private void checkCandidateName(String name) throws Exception {
        // 检查Redis中是否存在该候选人，如果不存在，则说明该候选人非法，抛出异常
        String key = VoteConstants.VOTE_CANDIDATESET;
        Boolean flag = redisTemplate.opsForSet().isMember(key, name);
        if (!flag){
            throw new Exception("候选人名字非法");
        }

    }

    @Override
    public List<VoteVo> getAllVote() {

        String key = VoteConstants.VOTE_DOVOTE_ZSET_KEY;
        Set<DefaultTypedTuple> data = redisTemplate.opsForZSet().rangeWithScores(key, 0, -1);

        // Set<DefaultTypedTuple> ---> List<VoteVo>
        ArrayList<VoteVo> voteVos = new ArrayList<>();
        for (DefaultTypedTuple tuple : data) {

            String name = (String) tuple.getValue();
            Double voteNum = tuple.getScore();
            VoteVo voteVo = new VoteVo();
            voteVo.setCandidateName(name);
            voteVo.setTotalnum(voteNum.longValue());

            voteVos.add(voteVo);
        }

        return voteVos;
    }

    @Override
    public VoteEchartVo voteListForEchart() {
        List<VoteVo> data = getAllVote();

        // List<VoteVo> ---> VoteEchartVo
        ArrayList<String> nameList = new ArrayList<>();
        ArrayList<Long> voteNumList = new ArrayList<>();

        for (VoteVo voteVo : data) {
            nameList.add(voteVo.getCandidateName());
            voteNumList.add(voteVo.getTotalnum());
        }

        VoteEchartVo voteEchartVo = new VoteEchartVo();
        voteEchartVo.setCandidates(nameList);
        voteEchartVo.setTotalvotenums(voteNumList);
        return voteEchartVo;
    }


    /**
     * 使用手机号给指定候选人投票，要求每个手机号每分钟只能投一票
     * 检查该手机号是否在一分钟内重复投票了
     * 如果重复投票了，就抛出异常
     * @param phone
     */
    private void checkCanVote(String phone) throws Exception {

        //1. 检查Redis中指定的key是否存在，如果存在就证明重复投票了
        String key = VoteConstants.VOTE_PHONE_PRE+phone;
        Boolean flag = redisTemplate.hasKey(key);
        if (flag){
            // 重复投票了，异常退出
            log.info("手机号{}在一分钟只能重复投票了，异常退出",phone);
            throw new Exception("重复投票了!");
        }else {
            // 记录在手机号到Redis中的一个String中
            redisTemplate.opsForValue().set(key,new Date(),60, TimeUnit.SECONDS);
            log.info("手机号{}可以正常投票",phone);
        }

    }
}
